11W - EKS에서 FSx, Inferentia 활용하기

개요

이번 주차에서는 클러스터 환경에서 머신 러닝 인프라를 구축하는 방법에 대해 알아본다.
이번에도 스터디 협력자 분께서 실습을 할 수 있는 워크샵을 제공해주셨기 때문에, 이것을 기반으로 먼저 실습을 진행한다.
이것 말고도 다른 내용도 있긴 한데, 시간이 부족해 아직 해보지는 못했고, 충분하게 내용을 정리한 후에 글을 올리고자 한다.

사전 지식

tranium, inferentia

AWS에서 제공하는 가속 컴퓨팅 인스턴스 타입이다.
AWS 자체적인 GPU 칩을 이용하여 각각 머신러닝 모델 훈련과 추론에 탁월한 성능을 발휘하도록 구축됐다.

Neuron SDK

Neuron SDK는 CUDA와 같은 GPU를 위한 하드웨어 툴킷이다.
CUDA는 엔비디아 한정으로 이용된다.
AWS에서는 자신들의 전용 AI 인스턴스인 Inferentia, Trainium을 위한 독자적인 sdk를 개발했다.
뉴론 sdk는 기본적으로 GPU 칩을 찾아 등록하고 활용할 수 있도록 도와준다.
또한 기존 ML 프레임워크인 파이토치, 텐서플로우와 잘 통합되어 있어 ML 엔지니어의 사용성을 크게 해치지 않는다.

FSx란?

FSx는 Amazon EFS와 같은 파일시스템 스토리지인데, 단순 NFS 이상으로 특화된 스토리지를 제공하는 리소스이다.
image.png
여기에는 Lustre 옵션이 있는데, 머신 러닝이나 높은 컴퓨팅 기능이 필요할 때 사용하는 옵션이다.
S3와의 연동이 지원되고, 일관적으로 밀리초 단위 지연을 넘어가지 않는 것이 보장되면서 초당 100기가의 처리량 등을 지원한다.

실습 진행

이번 실습은 위에서 말했듯이 스터디 조력자 분께서 워크샵으로 할 수 있게 도움을 주셨다.
그렇기에 이번에는 가급적 실습 세팅에 대한 정리는 생략하고자 한다.
구체적으로 전체 환경을 구성하고 실습을 하는 게 아니기도 하고, 스터디원으로서 얻은 기회로 실습을 할 수 있게 된 것이기 때문이다.

목표는 다음과 같다.

FSx for Lustre를 이용한 스토리지 구성

첫번째 단계로, 클러스터에서 사용할 수 있는 모델을 가져오는 단계가 필요하다.
사용하는 모델은 S3에 저장돼있어 이것을 LLM 모델이 가져올 수 있도록 하기 위한 설정이 필요하다.
이때 사용하는 것이 FSx for Lustre로 다음의 특징을 가지고 있다.

이 친구와 함께 라면 느린 오브젝트 스토리지도 옛말이다(친구비를 조금 내야 하긴 하지만)!
EKS 클러스터에서 활용하기 위한 CSI 드라이버 역시 제공한다.
그럼 본격적으로 FSx CSI 드라이버 세팅을 한다.

cat << EOF >  fsx-csi-driver.json
{
    "Version":"2012-10-17",
    "Statement":[
        {
            "Effect":"Allow",
            "Action":[
                "iam:CreateServiceLinkedRole",
                "iam:AttachRolePolicy",
                "iam:PutRolePolicy"
            ],
            "Resource":"arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"
        },
        {
            "Action":"iam:CreateServiceLinkedRole",
            "Effect":"Allow",
            "Resource":"*",
            "Condition":{
                "StringLike":{
                    "iam:AWSServiceName":[
                        "fsx.amazonaws.com"
                    ]
                }
            }
        },
        {
            "Effect":"Allow",
            "Action":[
                "s3:ListBucket",
                "fsx:CreateFileSystem",
                "fsx:DeleteFileSystem",
                "fsx:DescribeFileSystems",
                "fsx:TagResource"
            ],
            "Resource":[
                "*"
            ]
        }
    ]
}
EOF
aws iam create-policy \
        --policy-name Amazon_FSx_Lustre_CSI_Driver \
        --policy-document file://fsx-csi-driver.json
		
eksctl create iamserviceaccount \
    --region $AWS_REGION \
    --name fsx-csi-controller-sa \
    --namespace kube-system \
    --cluster $CLUSTER_NAME \
    --attach-policy-arn arn:aws:iam::$ACCOUNT_ID:policy/Amazon_FSx_Lustre_CSI_Driver \
    --approve

첫번째 과정은 역시 CSI 드라이버가 활용할 IAM 롤을 설정해주는 것이다. IRSA 방식을 이용한다.

export ROLE_ARN=$(aws cloudformation describe-stacks --stack-name "eksctl-${CLUSTER_NAME}-addon-iamserviceaccount-kube-system-fsx-csi-controller-sa" --query "Stacks[0].Outputs[0].OutputValue"  --region $AWS_REGION --output text)
echo $ROLE_ARN

이게 정상적으로 나오면 롤 설정이 완료된 것이다.

kubectl apply -k "github.com/kubernetes-sigs/aws-fsx-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.2"
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-fsx-csi-driver
kubectl annotate serviceaccount -n kube-system fsx-csi-controller-sa \
 eks.amazonaws.com/role-arn=$ROLE_ARN --overwrite=true

다음으로 CSI 드라이버를 배포하고, 해당 서비스 어카운트에 어노테이션을 부착한다.

image.png

FSXL_VOLUME_ID=$(aws fsx describe-file-systems --query 'FileSystems[].FileSystemId' --output text)
DNS_NAME=$(aws fsx describe-file-systems --query 'FileSystems[].DNSName' --output text)
MOUNT_NAME=$(aws fsx describe-file-systems --query 'FileSystems[].LustreConfiguration.MountName' --output text)

현재 사용할 수 있는 FSx는 이미 배포된 상태이다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: fsx-pv
spec:
  persistentVolumeReclaimPolicy: Retain
  capacity:
    storage: 1200Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  mountOptions:
    - flock
  csi:
    driver: fsx.csi.aws.com
    volumeHandle: FSXL_VOLUME_ID
    volumeAttributes:
      dnsname: DNS_NAME
      mountname: MOUNT_NAME

PV를 만들어줄 건데, 몇 가지 값들을 현재 배포된 FSx를 향하도록 세팅하고 적용한다.

sed -i'' -e "s/FSXL_VOLUME_ID/$FSXL_VOLUME_ID/g" fsxL-persistent-volume.yaml
sed -i'' -e "s/DNS_NAME/$DNS_NAME/g" fsxL-persistent-volume.yaml
sed -i'' -e "s/MOUNT_NAME/$MOUNT_NAME/g" fsxL-persistent-volume.yaml
kubectl apply -f fsxL-persistent-volume.yaml
kubectl apply -f fsxL-claim.yaml

image.png
성공적으로 배치됐다.

AI 워크로드 배포

이제 본격적으로 모델을 배포해본다.
먼저 카펜터를 이용해 워크로드가 스케줄링될 수 있는 노드 클래스를 등록한다.
image.png
인스턴스 패밀리가 인퍼런시아 2로 설정된 것을 볼 수 있다.

kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin-rbac.yml
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin.yml

노드가 배치되고 난 후에 노드의 GPU 자원을 활용할 수 있도록 뉴론 디바이스 플러그인을 설치해야 한다.

kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-scheduler-eks.yml
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/my-scheduler.yml

추가적으로 뉴론을 활용하는 파드를 스케줄링하는 뉴론 전용 스케줄러도 배포해준다.
image.png
스케줄러를 확인해봤는데, my scheduler는 확장자 스케줄러로서 뉴론 관련 자원을 토대로 스케줄링하는 역할을 수행한다.
이 스케줄러는 실제 스케줄링 동작을 수행할 뉴론 스케줄러 파드로 스케줄링 동작을 위임할 것이다.
image.png
워크로드를 배포했는데, 이미지 내려받는 것도 오래 걸리고 실행도 굉장히 오래 걸린다..
image.png
카펜터에서 미리 노드클래스와 풀을 정의해둬서 성공적으로 노드 클레임 오브젝트가 만들어지고 그 위에 워크로드가 배포되고 있다.
image.png
웹 ui도 배포해서 확인해본다.
아주 불친절한 친구다.
image.png
쉭쉭 아주 끈질기게 영어로 대답하는 친구다.

시스템 구조 확인

S3에 저장된 데이터

image.png
일단 FSx에서는 데이터 레포지토리로 s3를 가리키고 있다.
image.png
아직 정확한 개념은 모르겠으나, S3에서 데이터를 완전히 받아서 파일시스템으로 구축해둔 후 이걸 마운팅하는 구조로 이뤄진 것 같다.
그리고 서로 파일의 변경이 발생했을 때 import와 export를 수행하는 것으로 추측된다.
image.png
해당 S3에는 모델 파일 담겨있는 상태이다.
image.png
총 용량을 보니 27기가나 된다.
image.png
상세하게 들어가면 모델 바이너리에 데이터가 담겨있다.
모델 레이어 단위를 쪼개서 저장하는 건 해본 적이 없어서 구체적으로 잘은 모르겠는데, 당장 봤을 때는 각 계층의 가중치를 저장하는 방식으로 구성돼있는 것 같다.
image.png
FSx 콘솔에서 attach된 서브넷에 대한 정보를 찾을 수 없어 ec2 콘솔에서 찾아보니 인터페이스가 부착돼있긴 하다.

클러스터 컴포넌트 확인

image.png
확장자 스케줄러를 안 써봐서 잘 몰랐는데, 확장자 스케줄러 로그를 보면 기본 스케줄러가 스케줄링한 이벤트 정보를 전부 받아서 상태 동기화를 하는 작업이 있는 게 아닌가 싶다.
클러스터의 자원 상태를 확인해야 하니 당연한 작업 같기도 하다.
image.png
그런데 이 스케줄러가 하는 작업 자체를 상세하게 로그로 확인할 수는 없었다.

image.png
kube system에는 위에서 배포한 뉴론 디바이스 플러그인 관련 워크로드도 존재한다.
image.png
다음으로 본 것은 뉴론 디바이스 플러그인으로, 플러그인은 단순하게 kubelet을 거쳐 현재 노드의 자원을 클러스터가 인지할 수 있도록 업데이트하는 작업을 하는 모양이다.
image.png
실제로 파드에 마운팅을 하는 작업을 해주는 것은 뉴론 디바이스 플러그인 데몬셋으로, 컨테이너가 요구하는 자원을 그대로 마운팅하는 역할을 수행한다.
image.png
파드에는 뉴론 sdk를 활용하는 프로세스가 기본으로 위치하고, 마운팅된 경로에서 모델을 불러와 실행시킨다.
image.png
해당 경로에는 S3에서 본 파일들이 그대로 들어있다.
image.png
뉴론 디바이스가 장치로 부착된 것도 확인할 수 있다.
뉴론 프로세스의 인자로 설정했던 neuron0는 이걸 뜻하는 모양이다.

S3 크로스 리전 복제

간단하게 모델 데이터가 S3에 저장된 것을 확인했다.
재해 복구 시나리오나, 다양한 지역에서 이 S3를 사용할 수 있도록 멀티 리전 설정을 할 수 있다.
image.png
실습에서는 오하이오 쪽으로 하나의 버킷이 이미 생성돼있는데, 이것을 크로스 리전 설정해주면 된다.
이를 위해 데이터가 들어있는 S3에서 먼저 설정을 해준다.
image.png
테스트 용으로 test 접두사를 가진 오브젝트만 복제하도록 하고 대상 버킷을 지정한다.
실제로 모델 데이터를 복제하고 싶으면 해당 오브젝트가 복제되도록 설정하면 된다.
주의! 위 사진에서 실수로 접두사를 /test라 했는데, 테스트 디렉토리를 설정하고 싶으면 test/로 해야 한다.
아래 실습을 진행하다가 문제를 발견하고 수정했다.

image.png
버킷 복제를 위한 iam role을 지정하고, kms암호화도 설정한다.

이제 파드 안의 모델을 test 디렉토리로 복사해서 복제가 일어나는지 확인한다.

kubectl exec -it <YOUR-vLLM-POD-NAME> -- bash
cd /work-dir
mkdir test
cd test
cp /work-dir/Mistral-7B-Instruct-v0.2/README.md /work-dir/test/testfile
ls -ll /work-dir/test

간단하게 리드미 파일만 테스트 디렉토리에 담는다.
image.png
FSx를 통해 일단 test 디렉토리가 원본 S3에 export됐다.
image.png
위에서 접두사 세팅을 잘못해서 원래 왔어야 할 testfile은 안 오고, 내가 추가로 만든 test2 파일만 들어왔다.
image.png
이미 존재하는 파일을 복제하려면 replication job을 세팅해줘야 한다고 한다.
스스로 불러온 추가 실습 상황.. 그냥 잡을 만들어보려고 하니 iam 권한이 적절치 않다하여 문서를 참고했다.[1]
원본과 목적지 S3의 오브젝트와 버전 정보를 읽을 수 있는 권한과 목적지 버킷의 경우 데이터를 넣는 권한도 필요하다.
image.png
추가적으로 해당 role의 trust policy에는 배치 작업 리소스가 들어갈 수 있게 세팅해줘야 한다.
image.png
배치 작업을 수동으로 실행시키자 1분쯤 걸려서 작업이 완료됐다.
image.png
편안..

테스트 데이터 레이어 환경 만들기

위 실습에서는 정적 프로비저닝을 통해 PV를 만들었고, 이것은 관리자가 명확하게 리소스를 관리하는 운영환경에 적합한 방식이다.
그러나 테스트 환경에서는 ML 엔지니어가 동적으로 스토리지를 받을 수 있는 것이 좋다.
그래서 이번에는 동적 프로비저닝을 간단하게 해본다.
image.png
일단 스토리지 클래스를 만들어야 한다.
FSx 스토리지 클래스의 파라미터에는 바로 데이터를 가져올 S3 경로를 지정하는 것이 가능하다.
image.png
이 실습에서는 이렇게 세팅하지 않는다.
image.png
FSx에서는 동적 프로비저닝을 해주긴 하지만, 초기화에는 15분 정도가 소요된다고 한다.
image.png
CSI 컨트롤러의 로그를 보면 파일시스템이 만들어지는 과정을 추적하고 있는 것을 볼 수 있다.
image.png
보다시피 하나가 새로 만들어지고 있는데, 왜 FSx에서 연결된 서브넷 정보를 확인할 수 없었는지 알았다.
EFS CSI 드라이버는 하나의 파일 시스템에서 경로를 기반으로 스토리지 자원을 생성하여 분배해주는데 FSx CSI 드라이버는 각각 새로운 리소스를 생성한다.

성능 테스트

마지막으로 이 FSx라는 놈이 얼마나 잘난 놈인지, 스토리지 성능 테스트를 해본다.
FIO를 이용할 것이다.
image.png
어떤 이미지로 해도 상관은 없지만, FSx가 마운팅된 가용영역에 파드가 배치되도록 세팅하자.

apt-get update
apt-get install fio ioping -y

ioping -c 20 .

mkdir -p /data/performance
cd /data/performance
fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=fiotest --filename=testfio8gb --bs=1MB --iodepth=64 --size=8G --readwrite=randrw --rwmixread=50 --numjobs=8 --group_reporting --runtime=10

해당 파드에 들어가서 테스트를 진행해본다.
image.png
ioping을 한 결과인데, 실습에서는 그냥 루트 디렉토리에서 이 명령을 수행한다.
image.png
그러나 FSx가 마운팅된 경로에서 수행하면 결과가 조금 다르다.
이 결과를 보는 것이 실습 목표에 부합하지 않나 싶다.
아무튼 EBS로 부착됐을 인스턴스의 기본 스토리지보다 최소값이 훨씬 작고, 평균 값 역시 마찬가지이다.
다만 편차는 조금 더 큰 것을 확인할 수 있는데, 마이크로초 단위인 시점에서 충분히 준수한 성능이라고 생각한다.
image.png
fio를 통한 랜덤 읽기 쓰기 테스트 결과는 이렇다.
EFS와 같이 사용량에 맞춰 처리량이 증가하도록 돼있기에 , 크기가 커지면 더 속도도 올라가긴 할 것이다.
블록 사이즈를 무려 1메가로 잡아서 진행했는데, 모델 훈련 시에는 읽는 블록 단위를 크게 하기 때문에 적절한 테스트 방식이라고 한다(지피티 왈)
근데 워크샵 사진 정보와는 다르게, 처리량이 꽤나 작게 잡혔다.

결론

처음 AI를 공부하던 20년 당시에는 인프라 환경에 대해 일절 모르는 반푼이이기도 했지만서도 그 당시에 MLOps 일이 굉장한 전문성을 요구한다고 들었다.
대충 두어 시간 5퍼센트짜리 ML 엔지니어가 되어본 소감으로서는, 현재 기술 발전이 많이 돼서 과거에 생각했던 것만큼 세팅이 어렵지는 않은 것 같다.
근데 기술이 워낙 빠르게 변화하고 발전하다보니, 내가 잠시 팔로업 하지 못한 기간에 처음 들어보는 기술이 너무나도 많이 나온 것 같다;;
ML 도메인에서 일을 하게 된다면, 더더욱 사람들과 교류를 열심히 해야 한다는 생각이 든다.
단순히 혼자 공부하고 기술을 탐구하는 것으로는 이 변화의 흐름을 따라갈 수 없을 것이다.

한편으로, 쿠버네티스 1.32 버전 릴리즈 문서를 번역하면서 DRA에 대한 내용을 보았다.
DRA는 이제 막 베타 단계로 올라온 기능으로 클러스터에 다양한 자원을 등록하고 관리할 수 있도록 구조화된 파라미터를 지원한다.
현재 이 기술이 운영 환경에 접목되기 위해 얼마나 생태계가 구성되고 있나 궁금해서 검색해봤는데, 엔비디아에서 DRA 플러그인을 개발하고 있는 것을 볼 수 있었다.[2]
과연 이 기능이 언젠가 클러스터 환경에서 표준이 될지, 앞으로의 변화가 또 궁금해진다.
image.png
그나저나 워크샵에서 비용 조회 권한을 풀어준 덕분에 가격에 대한 부분을 볼 수 있었는데,역시 ㅎㄷㄷ하다.
전날 워크샵을 활성화해두고 금요일에 실습을 진행 중이라 현재 나온 값은 초기 환경에 대한 비용이라 할 수 있다.
ec2 비용이야 그러려니 하겠는데, fsx 비용이 엄청나다.

이전 글, 다음 글

다른 글 보기

이름 index noteType created
1W - EKS 설치 및 액세스 엔드포인트 변경 실습 1 published 2025-02-03
2W - 테라폼으로 환경 구성 및 VPC 연결 2 published 2025-02-11
2W - EKS VPC CNI 분석 3 published 2025-02-11
2W - ALB Controller, External DNS 4 published 2025-02-15
3W - kubestr과 EBS CSI 드라이버 5 published 2025-02-21
3W - EFS 드라이버, 인스턴스 스토어 활용 6 published 2025-02-22
4W - 번외 AL2023 노드 초기화 커스텀 7 published 2025-02-25
4W - EKS 모니터링과 관측 가능성 8 published 2025-02-28
4W - 프로메테우스 스택을 통한 EKS 모니터링 9 published 2025-02-28
5W - HPA, KEDA를 활용한 파드 오토스케일링 10 published 2025-03-07
5W - Karpenter를 활용한 클러스터 오토스케일링 11 published 2025-03-07
6W - PKI 구조, CSR 리소스를 통한 api 서버 조회 12 published 2025-03-15
6W - api 구조와 보안 1 - 인증 13 published 2025-03-15
6W - api 보안 2 - 인가, 어드미션 제어 14 published 2025-03-16
6W - EKS 파드에서 AWS 리소스 접근 제어 15 published 2025-03-16
6W - EKS api 서버 접근 보안 16 published 2025-03-16
7W - 쿠버네티스의 스케줄링, 커스텀 스케줄러 설정 17 published 2025-03-22
7W - EKS Fargate 18 published 2025-03-22
7W - EKS Automode 19 published 2025-03-22
8W - 아르고 워크플로우 20 published 2025-03-30
8W - 아르고 롤아웃 21 published 2025-03-30
8W - 아르고 CD 22 published 2025-03-30
8W - CICD 23 published 2025-03-30
9W - EKS 업그레이드 24 published 2025-04-02
10W - Vault를 활용한 CICD 보안 25 published 2025-04-16
11W - EKS에서 FSx, Inferentia 활용하기 26 published 2025-04-18
11주차 - EKS에서 FSx, Inferentia 활용하기 26 published 2025-05-11
12W - VPC Lattice 기반 gateway api 27 published 2025-04-27

관련 문서

이름 noteType created
EKS, ECS 비교 knowledge 2024-11-03
Amazon Fargate knowledge 2024-11-03
Amazon Elastic Kubernetes Service knowledge 2025-02-01
eksctl knowledge 2025-02-06
Karpenter knowledge 2025-03-04
EKS Automode knowledge 2025-04-23
E-파드 마운팅 recursiveReadOnly topic/explain 2025-02-27
S-vpc 설정이 eks 액세스 엔드포인트에 미치는 영향 topic/shooting 2025-02-07
T-마운트 전파 Bidirectioal topic/temp 2025-02-28

참고


  1. https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-batch-replication-policies.html ↩︎

  2. https://github.com/NVIDIA/k8s-dra-driver-gpu ↩︎